package cn.xeblog.plugin.util;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.nio.charset.Charset;

/**
 * 字符集工具类
 *
 * @author LYF
 * @date 2022-06-23
 */
public class CharsetUtils {

  /**
   * 近似判断文本文件的字符集，文件开头三个字节表明编码格式。
   */
  public static Charset charset(String path) {
    String charset = "GBK";
    byte[] first3Bytes = new byte[3];
    try {
      boolean checked = false;
      BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
      bis.mark(0);
      int read = bis.read(first3Bytes, 0, 3);
      if (read == -1) {
        bis.close();
        // 文件编码为 ANSI
        return Charset.forName(charset);
      } else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
        // 文件编码为 Unicode
        charset = "UTF-16LE";
        checked = true;
      } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
        // 文件编码为 Unicode big endian
        charset = "UTF-16BE";
        checked = true;
      } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB
          && first3Bytes[2] == (byte) 0xBF) {
        // 文件编码为 UTF-8
        charset = "UTF-8";
        checked = true;
      }
      bis.reset();
      if (!checked) {
        while ((read = bis.read()) != -1) {
          if (read >= 0xF0) {
            break;
          }
          // 单独出现BF以下的，也算是GBK
          if (0x80 <= read && read <= 0xBF) {
            break;
          }
          if (0xC0 <= read && read <= 0xDF) {
            read = bis.read();
            // 双字节 (0xC0 - 0xDF),也可能在GB编码内
            if (!(0x80 <= read && read <= 0xBF)) {
              break;
            }
          } else if (0xE0 <= read && read <= 0xEF) {
            read = bis.read();
            if (0x80 <= read && read <= 0xBF) {
              read = bis.read();
              if (0x80 <= read && read <= 0xBF) {
                charset = "UTF-8";
              }
            }
            break;
          }
        }
      }
      bis.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return Charset.forName(charset);
  }
}
